<?php
Yii::import('dao.bid.BidDao');
Yii::import('application.components.autobid.AuctionMarkDao');
Yii::import('dao.bidAuction.AuctionLimitDao');
Yii::import('application.components.auctionCache.customerCacheAuctionBidInfo');
Yii::import('application.components.auctionCache.AuctionHistoryList');
Yii::import('dao.customer.CustomerDao');
Yii::import('dao.auction.AuctionCacheData');
Yii::import('dao.auction.AuctionDao');
Yii::import('dao.auction.AuctionDetailDao');

class BidAuction extends CFormModel
{

    const LOOP_INTERVAL = 50000; 

    public $auctionId;

    public $customerId;

    public $timeOutLeft=1;

    protected $_retry = false;

    protected $_auction;

    protected $_customer;

    protected $_bidAfterInfo;

    protected $_bidDao;

    protected $_env = array();

    protected $_bidType;

    protected $_limitDao;

    public function setEnv($env, $on = true)
    {
        $this->_env[$env] = $on;
        return $this;
    }

    public function isEnv($env)
    {
        return array_key_exists($env, $this->_env) && $this->_env[$env];
    }

    public function init()
    {
        $this->_bidDao = new BidDao();
        $this->_limitDao = new AuctionLimitDao();
    }
    
    public function bid()
    {
        $timeOut = time() + $this->timeOutLeft;
        do {
            $retry = time() < $timeOut;
            if (! $this->isEnv('parentTransaction')) {
                $transaction = Yii::app()->db->beginTransaction();
            }
            try {
                
                if (! $this->validate()) {
                    throw new CException(Yii::t('yii', 'Bid Auction Validate'));
                }
                
                if (Yii::app()->lock->get('BID_AUCTION_LOCK:' . $this->auctionId, $this->_auction->end_time + 2)) {
                    usleep(self::LOOP_INTERVAL);
                    continue;
                }
                
                $this->bidBase();
                if (! $this->isEnv('parentTransaction')) {
                    $transaction->commit();
                }
                
                $auctionCache = new AuctionCacheData();
                $cacheData = array(
                    'winner_price' => $this->_bidAfterInfo['bid_after_winner_price'],
                    'winner_id' => $this->_bidAfterInfo['bid_after_winner_id'],
                    'winner_time' => $this->_bidAfterInfo['bid_after_winner_time'],
                    'end_time' => $this->_bidAfterInfo['bid_after_end_time'],
                    'winner_name' => $this->_customer->user_name,
                    'winner_avatar' => $this->_customer->avatar
                );
                $auctionCache->update($this->auctionId, $cacheData);
                
                $bidHistory = new AuctionHistoryList();
                $historyData = array(
                    'price' => $this->_bidAfterInfo['bid_after_winner_price'],
                    'winner' => $this->_customer->user_name,
                    'bidder_type' => $this->isEnv('autoBid') ? 'AUTO BID' : 'BID'
                );
                $bidHistory->setHistoryKey($this->_auction->auction_id);
                $bidHistory->addItem($this->_bidAfterInfo['bid_after_winner_price'], json_encode($historyData));
                
                if ($this->isEnv('autoBid')) {
                    $marker = new AuctionMarkDao();
                    $marker->auction_id = $this->auctionId;
                    $marker->add();
                }
                
                $customerBidInfoCache = new customerCacheAuctionBidInfo();
                $customerBidInfoCache->customerId = $this->customerId;
                $customerBidInfoCache->auctionId = $this->auctionId;
                $customerBidInfoCache->addItem($this->_bidType, $this->_bidAfterInfo['customerBidAuctionCost']);
                
                return true;
            } catch (Exception $e) {
                $retry = false;
                if (! $this->isEnv('parentTransaction')) {
                    $transaction->rollback();
                }
                if ($this->isEnv('throw')) {
                    throw new CException(Yii::t('yii', $e->getMessage()));
                }
                return false;
            } finally{
                Yii::app()->lock->del('BID_AUCTION_LOCK:' . $this->auctionId);
            }
        } while ($retry);
        
        $this->addError('timeOut', 'Bid Auction Time Out');
        if ($this->isEnv('throw')) {
            throw new CException(Yii::t('yii', 'Bid Auction Time Out'));
        }
        return false;
    }

    public function bidBase()
    {
        $endTime = $this->_auction->end_time;
        
        $lastTime = $this->_auction->end_time - time();
        
        switch (true) {
            case $lastTime <= 15:
                $endTime = time() + 15;
                break;
        }
        // update auction
        $updataAuctionSQL = '
            UPDATE  
                auction_pool
            SET
                auction_pool.winner_price   = @bid_after_winner_price   := auction_pool.winner_price + auction_pool.winner_price_increment,
                auction_pool.winner_id      = @bid_after_winner_id      := ' . $this->customerId . ',
                auction_pool.winner_time    = @bid_after_winner_time    := ' . time() . ',
                auction_pool.end_time       = @bid_after_end_time       := ' . $endTime . '
            WHERE
                auction_pool.auction_id = ' . $this->auctionId . '
        ';
        $updataAuction = Yii::app()->db->createCommand($updataAuctionSQL)->execute();
        
        // add limit
        $auctionLimit = new AuctionLimitDao();
        $auctionLimit->auctionId = $this->auctionId;
        $auctionLimit->customerId = $this->customerId;
        $auctionLimit->add($this->_auction->end_time);
        
        $getBidAfterSQL = '
            SELECT
                @bid_after_winner_price AS bid_after_winner_price,
                @bid_after_winner_id AS bid_after_winner_id,
                @bid_after_winner_time AS bid_after_winner_time,
                @bid_after_end_time AS bid_after_end_time
        ';
        
        $this->_bidAfterInfo = Yii::app()->db->createCommand($getBidAfterSQL)->queryRow();
        $this->_bidAfterInfo['bidHistoryId'] = 0;
        
        // bid cost
        $this->_bidDao->customerId = $this->customerId;
        $this->_bidDao->auctionId=$this->auctionId;
        $cash = $this->_bidDao->getBidModel('cash')->getBid();
        if ($cash > 0) {
            $this->_bidType = 'cash';
            $bidModel = $this->_bidDao->getBidModel('cash');
        } else {
            $this->_bidType = 'promo';
            $bidModel = $this->_bidDao->getBidModel('promo');
        }
        
        $bidSourceAction = 'BID_AUCTION';
        if ($this->isEnv('autoBid')) {
            $bidSourceAction = 'AUTO_BID';
        }
        
        $bidModel->setEnv('parentTransaction');
        $setBid = $bidModel->setBid(- 1,$bidSourceAction , 'BID_AUCTION:' . $this->auctionId);
        if (! $setBid) {
            $this->addErrors($bidModel->getErrors());
            throw new CException(Yii::t('yii', 'Bid Auction Cost Bid Error'));
        }
        
        $bidModel->component('auctionCost')
            ->setAucitonId($this->auctionId)
            ->cost();
        
        $bidAuctionCost = $bidModel->component('auctionCost')->getBidAuctionCost();
        $this->_bidAfterInfo['customerBidAuctionCost'] = $bidAuctionCost;
        
        // add customer Cost
        
        $this->_bidAfterInfo['bidHistoryId'] = $bidModel->getLastHistoryId();
        
        $action = 'BID';
        if ($this->isEnv('autoBid')) {
            $action = 'AUTO_BID';
        }
        
        // add bid log
        if(empty($this->_bidAfterInfo['bid_after_winner_price'])){
            throw new CException(Yii::t('yii', 'Auction End'));
        }
        $auctionBidLog = new AuctionBidLog();
        $auctionBidLog->attributes = array(
            'auction_id' => $this->auctionId,
            'winner_price' => $this->_bidAfterInfo['bid_after_winner_price'],
            'winner_name' => $this->_customer->user_name,
            'action' => $action,
            'customer_id' => $this->customerId,
            'bid_source_type' => $bidSourceAction,
            'amount' => -1,
            'bid_type' => strtoupper($this->_bidType),
            'action_history' => 'COST',
            'note' => 'BID_AUCTION:' . $this->auctionId,
            'created' => time(),
        );
        
        $auctionBidLog->save();
        if(Yii::app()->lock->check('AUCTION_END_LOCK:' . $this->auctionId)){
            throw new CException(Yii::t('yii', 'Auction End'));
        }
    }

    public function getBidAfterInfo()
    {
        return $this->_bidAfterInfo;
    }

    public function getAuctionInfo()
    {
        $auctionPool = new AuctionPool();
        $auctionInfo = $auctionPool->findByAttributes(array(
            'auction_id' => $this->auctionId,
            'status' => 1
        ));
        $this->_auction = $auctionInfo;
        return $this->_auction;
    }

    public function rules()
    {
        return array(
            array(
                'auctionId,customerId',
                'required'
            ),
            array(
                'auctionId',
                'hasAuction'
            ),
            array(
                'auctionId',
                'hasBuyItnow'
            ),
            array(
                'auctionId',
                'hasLimit'
            ),
            array(
                'customerId',
                'hasCustomer'
            ),
            array(
                'customerId',
                'notWinner'
            ),
            array(
                'customerId',
                'hasBids'
            ),
            array(
                'auctionId',
                'isRookieAuction'
            ),
            array(
                'customerId',
                'IsOverValuePrirce'
            ),
            array(
                'customerId',
                'checkTypeLimits'
            )
        );
    }
    
    //check customer cost 
    public function IsOverValuePrirce($attribute, $params){
        $auction=new AuctionDetailDao();
        $auction->find($this->auctionId);
        
        if ($auction->status!=2){
            $auctionPool=AuctionPool::model()->findByAttributes(array(
                'auction_id'=>$this->auctionId
            ));
            $auction->winner_price=$auctionPool->winner_price;
        }
         
        $customer=Customer::model()->findByPk($this->customerId);
        $userName=$customer->user_name;
        
        $bidLog=new AuctionBidLog();
        $bidLogNum=$bidLog->countByAttributes(array(
            'winner_name'=>$userName,
            'auction_id'=>$this->auctionId
        ));
        
        $bids=$bidLogNum;
        $costBidPrice=floatval(($bidLogNum+1)*0.5);
        $winnerPrice=floatval($auction->winner_price+$auction->winner_price_increment);
        $cost=$costBidPrice+$winnerPrice;
        $valuePrice=floatval($auction->value_price);
        if ($cost>$valuePrice){
            $this->addError('overcost', "Your costs is over value price,you can buy it now!");
        }
    }
    
    /**
     *  不同类型Auction对应不同的limit限制次数 
     *  bronze 5 ,silver 4,gold 3,bidpack 6,regular 15
     */
    public function  checkTypeLimits($attribute, $params){
        $auction=new AuctionDao();
        $auction->find($this->auctionId);
        $typeLimits=array(15,6,4,3,2);
        $types=array('regular auction','bid pack','bronze auction','silver auction','gold auction');
        $limitNum=15;
        $type='regular';
        if (!empty($auction) && $auction->type>=1){
            $limitNum=$typeLimits[$auction->type-1];
            $type=$types[$auction->type-1];
        }
        
        $liveLimitSql='
                    SELECT
                    	COUNT(auction_limit.auction_id) as liveLimit
                    FROM
                    	auction_limit
                    LEFT JOIN auction_parame ON auction_limit.auction_id = auction_parame.auction_id
                    LEFT JOIN auction_pool ON auction_pool.auction_id=auction_limit.auction_id
                    WHERE
                    	auction_parame.parame_name = "type"
                    AND 
                        auction_parame.parame_value = '.$auction->type.'
                    AND 
                        auction_limit.winner_id = '.$this->customerId.'
                    AND
                        auction_pool.`status`=1
                    AND
                        auction_limit.limit_type NOT  IN("BUY_IT_NOW","AUTO_BID")
                ';
        $liveLimitNum=Yii::app()->db->createCommand($liveLimitSql)->queryRow();
        
        $closeLimitSql='
                    SELECT
                    	COUNT(orders.auction_id) as closeLimit
                    FROM
                    	orders
                    LEFT JOIN auction_parame ON orders.auction_id = auction_parame.auction_id
                    WHERE
                    	auction_parame.parame_name = "type"
                    AND
                        auction_parame.parame_value = '.$auction->type.'
                    AND 
                        orders.customer_id = '.$this->customerId.'
                    AND
                        orders.created>='.(time()-2592000).'
                    AND
                        orders.type=1
                ';
        $closeLimitNum=Yii::app()->db->createCommand($closeLimitSql)->queryRow();
        if($liveLimitNum['liveLimit']+$closeLimitNum['closeLimit']>=$limitNum){
            $this->addError($attribute,"You are at your ".$type.' win limit.');
        }
    }
    
    //check rookie rule
    public function isRookieAuction($attribute, $params){
        $auction=new AuctionDao();
        $auction->find($this->auctionId);
        $checkTypes=[3,4,5];
        //检测是否为rookie以及是否是金银铜类型auction
        if ($auction->rookie && in_array($auction->type, $checkTypes)){
            $orders=Orders::model()->findAllByAttributes(array(
                'customer_id'=>$this->customerId,
                'type'=>1
            ));
            foreach($orders as $order){
                $auctionDao=new AuctionDao();
                $auctionDao=$auctionDao->find($order->auction_id);
                //如果已经生成过金银铜的订单，不能bidrookie auction
                if (in_array($auctionDao->type, $checkTypes)){
                    $this->addError($attribute, 'You can not bid Rookie Auction!');
                    break;
                }
            }
        }
    }
    
    public function hasBuyItnow($attribute, $params)
    {
        $this->_limitDao->customerId = $this->customerId;
        $this->_limitDao->auctionId = $this->auctionId;
        $limitNumber = $this->_limitDao->limit(array(
            'type' => 'BUY_IT_NOW',
            'auction' => $this->auctionId
        ));
        if ($limitNumber > 0) {
            $this->addError('buy_it_now', 'You already have purchased this item!');
        }
    }

    public function hasLimit($attribute, $params)
    {
        $this->_limitDao->customerId = $this->customerId;
        $this->_limitDao->auctionId = $this->auctionId;
        $limitNumber = $this->_limitDao->limit();
        if ($limitNumber >= 3) {
            $this->addError('buy_it_now', 'Your limit overflows');
        }
    }

    public function hasBids($attribute, $params)
    {
        $this->_bidDao->customerId = $this->customerId;
        if ($this->_bidDao->getBid() <= 0) {
            $this->addError($attribute, 'Have no bids!');
        }
    }

    public function notWinner($attribute, $params)
    {
        if (empty($this->_auction)) {
            return false;
        }
        if ($this->_auction->winner_id == $this->customerId) {
            $this->addError('is_winner', 'Cannot place bid. You are the highest bidder.');
        }
    }

    public function getCustomerInfo()
    {
        $customerInfo = new CustomerDao();
        $customerInfo = $customerInfo->find($this->customerId);
        $this->_customer = $customerInfo;
        return $this->_customer;
    }

    public function hasCustomer($attribute, $params)
    {
        if (empty($this->customerId)) {
            return false;
        }
        $customerInfo = $this->getCustomerInfo();
        if (empty($customerInfo)) {
            $this->addError($attribute, 'You are no longer logged in. Please log in again!');
        }
    }

    public function hasAuction($attribute, $params)
    {
        if (empty($this->auctionId)) {
            return false;
        }
        $auctionInfo = $this->getAuctionInfo();
        if (empty($auctionInfo)) {
            $this->addError($attribute, 'Auction is no longer available!');
        }
    }
}